package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.jsola.common.StrKit;
import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.IEmpAttendanceDAO;
import org.jsola.hr.dto.EmpAttendanceAddDTO;
import org.jsola.hr.dto.EmpAttendanceUpdateDTO;
import org.jsola.hr.entity.CompanyDO;
import org.jsola.hr.entity.EmpAttendanceDO;
import org.jsola.hr.query.EmpAttendanceQuery;
import org.jsola.hr.service.IChannelService;
import org.jsola.hr.service.ICompanyService;
import org.jsola.hr.service.IEmpAttendanceService;
import org.jsola.hr.service.IEmpInfoService;
import org.jsola.hr.vo.ChannelVO;
import org.jsola.hr.vo.EmpAttendanceListVO;
import org.jsola.hr.vo.EmpAttendanceVO;
import org.jsola.hr.vo.EmpInfoVO;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 员工考勤表
 *
 * @author zhr
 */
@Slf4j
@Service("hrEmpAttendanceServiceImpl")
public class EmpAttendanceServiceImpl implements IEmpAttendanceService {

    @Autowired
    private IEmpAttendanceDAO empAttendanceDAO;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private IChannelService channelService;

    @Autowired
    private ICompanyService companyService;

    /**
     * 最大出勤天数
     */
    private static final String MAX_WORK_DAY = "10";

    /**
     * 最大今日工时
     */
    private static final String MAX_WORK_TIME = "999";

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public EmpAttendanceVO save(EmpAttendanceAddDTO empAttendanceAddDTO, TokenUser tokenUser) {

        // 校验本天是否存在考勤数据
        EmpAttendanceDO empAttendanceDO1 = selectByEmpId(empAttendanceAddDTO.getYearly(),
                empAttendanceAddDTO.getMonthly(), empAttendanceAddDTO.getDaily(),
                empAttendanceAddDTO.getEmpId(), tokenUser.getSiteId());
        if (Objects.nonNull(empAttendanceDO1)) {
            throw new ParamException("当天已存在考勤数据,请更新");
        }
        checkWorkDay(empAttendanceAddDTO.getDayWorkTime(), empAttendanceAddDTO.getWorkDay());
        // 转DO
        EmpAttendanceDO empAttendanceDO = empAttendanceAddDTO.to(EmpAttendanceDO.class);
        // 编辑人
        empAttendanceDO.setEditBy(tokenUser.getUserName());
        // 保存
        empAttendanceDO = save(empAttendanceDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return empAttendanceDO.to(EmpAttendanceVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(EmpAttendanceUpdateDTO empAttendanceUpdateDTO, TokenUser tokenUser) {
        checkWorkDay(empAttendanceUpdateDTO.getDayWorkTime(), empAttendanceUpdateDTO.getWorkDay());
        //转DO
        EmpAttendanceDO empAttendanceDO = empAttendanceUpdateDTO.to(EmpAttendanceDO.class);
        // 编辑人
        empAttendanceDO.setEditBy(tokenUser.getUserName());
        //根据主键更新，只更新非null值
        return updateByIdSelective(empAttendanceDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    /**
     * 校验工时，出勤天数
     *
     * @param dayWorkTime 当天工时
     * @param workDay     出勤天数
     */
    private void checkWorkDay(Float dayWorkTime, Float workDay) {
        // 校验工时
        if (new BigDecimal(dayWorkTime + "").compareTo(BigDecimal.ZERO) < 0) {
            throw new ParamException("工时范围：0 ~ 999");
        }
        if (new BigDecimal(dayWorkTime + "").compareTo(new BigDecimal(MAX_WORK_TIME)) > 0) {
            throw new ParamException("工时范围：0 ~ 999");
        }
        // 校验出勤天数
        if (new BigDecimal(workDay + "").compareTo(BigDecimal.ZERO) < 0) {
            throw new ParamException("出勤天数：0 ~ 10");
        }
        if (new BigDecimal(workDay + "").compareTo(new BigDecimal(MAX_WORK_DAY)) > 0) {
            throw new ParamException("出勤天数：0 ~ 10");
        }
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, Long... empAttendanceIds) {
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) empAttendanceIds);
    }


    @Override
    public EmpAttendanceVO selectById(Long empAttendanceId, String siteId) {
        EmpAttendanceDO empAttendanceDO = selectDOById(empAttendanceId, siteId);
        if (empAttendanceDO == null) {
            return null;
        }
        return empAttendanceDO.to(EmpAttendanceVO.class);
    }

    @Override
    public List<EmpAttendanceListVO> select(EmpAttendanceQuery empAttendanceQuery, String siteId) {
        List<EmpAttendanceDO> empAttendanceDOList = selectDO(empAttendanceQuery, siteId);
        if (CollectionUtils.isEmpty(empAttendanceDOList)) {
            return empAttendanceDOList == null ? null : new ArrayList<>();
        }
        return empAttendanceDOList.stream()
                .map(empAttendanceDO -> empAttendanceDO.to(EmpAttendanceListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public List<EmpAttendanceDO> selectByEmpIdList(Integer yearly, Integer monthly, Integer daily,
                                                   List<String> companyIdList, List<String> empIdList, String siteId) {
        if (CollectionUtils.isEmpty(companyIdList) || CollectionUtils.isEmpty(empIdList)) {
            return new ArrayList<>();
        }
        Example example = new Example(EmpAttendanceDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId)
                .andEqualTo("yearly", yearly)
                .andEqualTo("monthly", monthly)
                .andEqualTo("daily", daily)
                .andIn("companyId", companyIdList)
                .andIn("empId", empIdList);

        return empAttendanceDAO.selectByExample(example);
    }

    @Override
    public int selectCount(EmpAttendanceQuery empAttendanceQuery, String siteId) {
        Example example = buildExample(empAttendanceQuery, siteId);
        return empAttendanceDAO.selectCountByExample(example);
    }

    @Override
    public Page<EmpAttendanceListVO> selectPage(EmpAttendanceQuery empAttendanceQuery, String siteId) {

        Page<EmpInfoVO> page = empInfoService.pageOutsource(empAttendanceQuery.getCompanyIdList(), empAttendanceQuery.getNameOrPhone(),
                empAttendanceQuery.getChannelId(), empAttendanceQuery.getPageNo(), empAttendanceQuery.getPageSize(), siteId);
        List<EmpInfoVO> empInfoVOS = page.getEntities();
        if (CollectionUtils.isEmpty(empInfoVOS)) {
            return new Page<>(empAttendanceQuery.getPageSize(), empAttendanceQuery.getPageNo());
        }
        // 总条数
        int entityCount = page.getEntityCount();
        List<EmpAttendanceListVO> list = new ArrayList<>();
        // 渠道id集合
        List<String> channelIdList = empInfoVOS.parallelStream().map(EmpInfoVO::getChannelId)
                .distinct().collect(Collectors.toList());
        List<ChannelVO> channelVOS = channelService.selectByIds(channelIdList, siteId);
        // 渠道id-渠道信息 Map
        Map<String, ChannelVO> idChannelVOMap = channelVOS.parallelStream()
                .collect(Collectors.toMap(ChannelVO::getId, v -> v));

        // 用工单位id集合
        List<String> companyIdList = empInfoVOS.parallelStream().map(EmpInfoVO::getCompanyId)
                .distinct().collect(Collectors.toList());
        List<CompanyDO> companyDOS = companyService.selectBaseCompanyByIdList(companyIdList, siteId);
        // 用工单位id-用工单位信息 Map
        Map<String, CompanyDO> idCompanyMap = companyDOS.parallelStream()
                .collect(Collectors.toMap(CompanyDO::getId, v -> v));

        // 员工id集合
        List<String> empIdList = empInfoVOS.parallelStream().map(EmpInfoVO::getId)
                .distinct().collect(Collectors.toList());
        // 所有的考勤工时记录
        List<EmpAttendanceDO> empAttendanceDOS = selectByEmpIdList(empAttendanceQuery.getYearly(),
                empAttendanceQuery.getMonthly(), empAttendanceQuery.getDaily(), companyIdList, empIdList, siteId);
        // 员工id-考勤工时记录分组
        Map<String, List<EmpAttendanceDO>> empIdAttendanceMap = empAttendanceDOS.parallelStream()
                .collect(Collectors.groupingBy(EmpAttendanceDO::getEmpId, Collectors.toList()));

        for (EmpInfoVO empInfoVO : empInfoVOS) {
            String empId = empInfoVO.getId();
            empAttendanceQuery.setEmpId(empId);
            String channelId = empInfoVO.getChannelId();
            if (StrKit.isNotEmpty(channelId)) {
                ChannelVO channelVO = idChannelVOMap.get(channelId);
                if (Objects.nonNull(channelVO)) {
                    empInfoVO.setChannelVO(channelVO);
                }
            }
            String companyId = empInfoVO.getCompanyId();
            if (StrKit.isNotEmpty(companyId)) {
                CompanyDO companyDO = idCompanyMap.get(companyId);
                if (Objects.nonNull(companyDO)) {
                    empInfoVO.setCompanyName(companyDO.getName());
                }
            }
            empAttendanceQuery.setCompanyId(empInfoVO.getCompanyId());
            List<EmpAttendanceDO> listVOS = empIdAttendanceMap.get(empId);
            EmpAttendanceListVO empAttendanceListVO = new EmpAttendanceListVO();
            if (!CollectionUtils.isEmpty(listVOS)) {
                if (listVOS.size() == 1) {
                    empAttendanceListVO = listVOS.get(0).to(EmpAttendanceListVO.class);
                } else {
                    log.error("查询到{}条考勤记录,员工id:{},查询记录:{}", listVOS.size(), empAttendanceQuery.getEmpId(), JSON.toJSONString(listVOS));
                }
            }
            empAttendanceListVO.setEmpInfoVO(empInfoVO);
            // 设置
            getSumWorkTime(empAttendanceListVO, empInfoVO.getId(), empAttendanceQuery.getYearly(), empAttendanceQuery.getMonthly());
            list.add(empAttendanceListVO);

        }
        Page<EmpAttendanceListVO> empAttendanceListVOPage = new Page<>(list, empAttendanceQuery.getPageNo(),
                empAttendanceQuery.getPageNo(), list.size());
        // 考勤分页总条数设置为员工分页总条数
        empAttendanceListVOPage.setEntityCount(entityCount);
        return empAttendanceListVOPage;
    }

    /**
     * 计算当月累计工时和累计出勤天数
     */
    private void getSumWorkTime(EmpAttendanceListVO empAttendanceListVO, String empId, Integer yearly, Integer monthly) {
        if (Objects.isNull(empAttendanceListVO)) {
            return;
        }
        // 累计工时
        Float sumWorkTime = 0F;
        // 累计出勤天数
        Float sumWorkDay = 0F;
        List<EmpAttendanceDO> listVOS = getSumWorkTime(empId, yearly, monthly);
        for (EmpAttendanceDO listVO : listVOS) {
            sumWorkTime += listVO.getDayWorkTime();
            sumWorkDay += listVO.getWorkDay();
        }
        empAttendanceListVO.setSumWorkTime(sumWorkTime);
        empAttendanceListVO.setSumWorkDay(sumWorkDay);
    }

    /**
     * 获取指定员工,指定年月累计工时
     */
    private List<EmpAttendanceDO> getSumWorkTime(String empId, Integer yearly, Integer monthly) {
        Example example = new Example(EmpAttendanceDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("empId", empId)
                .andEqualTo("yearly", yearly)
                .andEqualTo("monthly", monthly);
        return empAttendanceDAO.selectByExample(example);
    }


    @Override
    public EmpAttendanceDO selectDOById(Long empAttendanceId, String siteId) {
        return listById(empAttendanceId, siteId);
    }

    @Override
    public List<EmpAttendanceDO> selectDO(EmpAttendanceQuery empAttendanceQuery, String siteId) {
        Example example = buildExample(empAttendanceQuery, siteId);
        return empAttendanceDAO.selectByExample(example);
    }

    @Override
    public EmpAttendanceDO selectByEmpId(Integer yearly, Integer monthly, Integer daily, String empId, String siteId) {
        Example example = new Example(EmpAttendanceDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId)
                .andEqualTo("yearly", yearly)
                .andEqualTo("monthly", monthly)
                .andEqualTo("daily", daily)
                .andEqualTo("empId", empId);
        return empAttendanceDAO.selectOneByExample(example);
    }


    /**
     * 根据查询参数，构建example
     *
     * @param empAttendanceQuery 查询参数
     * @param siteId             所属站点id
     * @return example
     */
    private Example buildExample(EmpAttendanceQuery empAttendanceQuery, String siteId) {
        Example example = new Example(EmpAttendanceDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (StrKit.isNotEmpty(empAttendanceQuery.getCompanyId())) {
            example.and()
                    .andEqualTo("companyId", empAttendanceQuery.getCompanyId());
        }
        if (StrKit.isNotEmpty(empAttendanceQuery.getEmpId())) {
            example.and()
                    .andEqualTo("empId", empAttendanceQuery.getEmpId());
        }
        if (StrKit.isNotEmpty(empAttendanceQuery.getYearly())) {
            example.and()
                    .andEqualTo("yearly", empAttendanceQuery.getYearly());
        }
        if (StrKit.isNotEmpty(empAttendanceQuery.getMonthly())) {
            example.and()
                    .andEqualTo("monthly", empAttendanceQuery.getMonthly());
        }
        if (StrKit.isNotEmpty(empAttendanceQuery.getDaily())) {
            example.and()
                    .andEqualTo("daily", empAttendanceQuery.getDaily());
        }
        // 排序
        ExampleKit.setExampleOrder(example, empAttendanceQuery.getOrders());
        return example;
    }
}




